Skip to content

feat(inbox): show live PR status on pull request list cards#2695

Merged
andrewm4894 merged 3 commits into
mainfrom
andy/inbox-list-pr-status
Jun 16, 2026
Merged

feat(inbox): show live PR status on pull request list cards#2695
andrewm4894 merged 3 commits into
mainfrom
andy/inbox-list-pr-status

Conversation

@andrewm4894

@andrewm4894 andrewm4894 commented Jun 16, 2026

Copy link
Copy Markdown
Member

Problem

Follow-up to #2694 (merged), which added a live PR-status badge to the pull request detail view. This brings that status to the list cards in the Pulls tab, plus addresses review feedback from both PRs.

The naive approach (a usePrDetails call per card) would be an N+1: one gh api REST call per visible PR, and GitHub is rate-limited.

Changes

Live status on list cards (no extra API calls)

  • workspace-server — the getPrDiffStatsBatch GraphQL query already fetches diff stats for every visible PR in one request. Added state isDraft to the same fragment and normalised GitHub's OPEN | CLOSED | MERGED enum into the { state, merged, draft } shape the badge expects. Zero additional requests.
  • core + workspace-server schemas — added state/merged/draft to prDiffStatsSchema (both copies).
  • ReportImplementationPrLink — now reads status from the surrounding PrDiffStatsBatchContext when present (list), falling back to the per-PR usePrDetails query only when there's no batch provider (detail view) — mirroring how PrDiffStats already works.
  • PullRequestCard — renders the status badge, stacked above the line-diff to use horizontal space efficiently.

Review feedback addressed (from #2694 + #2695 bots)

  • Unknown-state handling (Codex) — getPrDetailsByUrl returns { state: "unknown" } on failure; the badge now renders nothing for any unresolved state instead of a misleading green "open".
  • URL validation (Codex) — the badge validates via parsePrUrl (the same gate as "Open in GitHub") and renders nothing for non-canonical/unsafe URLs, so it never exposes an unvalidated external link.
  • Tooltip loading guard (Greptile) — the tooltip is guarded by isLoading so it doesn't show default values while pending.
  • state as z.enum (Greptile) — state is now z.enum(["open","closed","merged"]) in both schema copies; the server maps GitHub's GraphQL enum with a safe "open" fallback so an unexpected value can't fail validation for the whole batch.

How did you test this?

  • biome check + typecheck clean across @posthog/core, @posthog/workspace-server, and @posthog/ui.
  • No tests reference the batch shape; schema change is additive.
  • Local manual verification in the running app (by the author). Note: this touches workspace-server, so the app needs a full restart (not just renderer HMR) to serve the new GraphQL fields.

Notes

Automatic notifications

  • Publish to changelog?
  • Alert Sales and Marketing teams?

@andrewm4894 andrewm4894 self-assigned this Jun 16, 2026
@github-actions

github-actions Bot commented Jun 16, 2026

Copy link
Copy Markdown

React Doctor found no issues in the changed files. 🎉

Reviewed by React Doctor for commit eec0661.

@greptile-apps

greptile-apps Bot commented Jun 16, 2026

Copy link
Copy Markdown
Contributor
Prompt To Fix All With AI
Fix the following 2 code review issues. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 2
packages/core/src/git/router-schemas.ts:363-364
The `state` field is typed as `z.string()`, which means Zod will accept any string at runtime. The badge logic elsewhere does an exact comparison against `"closed"` (and implicitly relies on `"open"` / `"merged"` being the only other values). An unexpected string from the GraphQL layer — say, a future GitHub enum value or a data bug — silently falls through to the green "open" styling. A `z.enum` narrows the type, enables exhaustive matching, and catches bad values at the boundary.

```suggestion
  /** Lowercased GitHub PR state. */
  state: z.enum(["open", "closed", "merged"]),
```

### Issue 2 of 2
packages/workspace-server/src/services/git/schemas.ts:319-320
Same concern as in `@posthog/core/git/router-schemas` — the mirrored copy should also use `z.enum` so any unexpected GraphQL value is rejected at the server boundary before it reaches the client.

```suggestion
  /** Lowercased GitHub PR state. */
  state: z.enum(["open", "closed", "merged"]),
```

Reviews (1): Last reviewed commit: "feat(inbox): show live PR status on pull..." | Re-trigger Greptile

Comment thread packages/core/src/git/router-schemas.ts Outdated
Comment thread packages/workspace-server/src/services/git/schemas.ts Outdated
Base automatically changed from andy/inbox-live-pr-status to main June 16, 2026 09:29

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6888cbd727

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Extend the batched diff-stats GraphQL request to also fetch each PR's
state/isDraft, so list cards render live status (open/draft/merged/closed)
with no extra API calls. ReportImplementationPrLink now reads status from
the batch context when present and only falls back to the per-PR usePrDetails
query on the standalone detail view.
- Narrow PR state to a z.enum(['open','closed','merged']) in both schema
  copies so unexpected GraphQL values are caught at the boundary; the
  workspace-server maps GitHub's enum with a safe 'open' fallback.
- Harden ReportImplementationPrLink: validate the URL via parsePrUrl
  (no unvalidated external link), render nothing for unknown/unresolved
  state instead of a misleading green 'open', and guard the tooltip with
  isLoading.
- Stack the status badge above the line-diff on the card to use horizontal
  space more efficiently.
@andrewm4894 andrewm4894 force-pushed the andy/inbox-list-pr-status branch from 6888cbd to 23c9e0d Compare June 16, 2026 09:35
The Pulls list reads status from the batched getPrDiffStatsBatch query,
which usePrActions wasn't invalidating — only the per-PR getPrDetailsByUrl
cache. Invalidate the batch on a successful PR action so the list badge
doesn't show stale status until the 5-min cache expires.
@andrewm4894 andrewm4894 added the Stamphog This will request an autostamp by stamphog on small changes label Jun 16, 2026
@andrewm4894 andrewm4894 enabled auto-merge (squash) June 16, 2026 09:44

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clean, focused feature that adds PR status to the batched GraphQL request; all substantive bot-raised concerns (z.enum typing, cache invalidation) were addressed in subsequent commits and confirmed in the current diff.

@andrewm4894 andrewm4894 merged commit 2b4cf36 into main Jun 16, 2026
26 checks passed
@andrewm4894 andrewm4894 deleted the andy/inbox-list-pr-status branch June 16, 2026 09:51
Gilbert09 added a commit that referenced this pull request Jun 16, 2026
…ort #2695, #2694)

Inbox signal reports that have an `implementation_pr_url` now render a live
PR-status badge (open / merged / closed / draft) on the report list rows, the
tinder card, and the report detail screen — reusing the existing
`usePrStatus` hook and `PrStatusBadge` component from the tasks feature.

The badge only renders for a canonical GitHub PR URL and renders nothing
while the state is loading or unresolvable (private repo / 404 / unparseable),
rather than showing a misleading default "open" badge. This is gated by a new
`hideWhenUnresolved` prop on `PrStatusBadge`; the existing task-header usage
keeps its always-tappable neutral badge. A `size="sm"` variant keeps the badge
compact on dense list rows.

Ports the desktop behavior from #2695 (list cards) and #2694 (detail) to the
mobile app. The desktop GraphQL batching is intentionally not ported; mobile
relies on react-query caching of the public GitHub REST API.

Generated-By: PostHog Code
Task-Id: ba8e678f-1c24-4c21-abe3-5dc204adec1a
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Stamphog This will request an autostamp by stamphog on small changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant